﻿// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This file is part of CodingEditor.
// Note:	This project is derived from Peter Project
//			(hosted on sourceforge and codeplex)
//
// Copyright (c) 2008-2009, CE Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


namespace CE.Main
{
	using System;
    using System.IO;
	using System.Text.RegularExpressions;
	using System.Windows.Forms;

	using Be.Windows.Forms;

	using CE.API;
	using CE.CustomControls;

	using WeifenLuo.WinFormsUI.Docking;

	public class HexEditor : DockContent, ICEPluginTab
	{
		#region Fields

		private string _fileName;
		private byte[] _findBuffer = new byte[0];
		private ToolStripStatusLabel fileSizeToolStripStatusLabel;
		private HexBox hexBox;
		private NumericUpDown nup;
		private OpenFileDialog openFileDialog;
		private SaveFileDialog saveFileDialog;
		private StatusStrip statusStrip1;
		private ToolStripSeparator toolStripSeparator1;
		private ToolStripSeparator toolStripSeparator2;
		private ToolStripSeparator toolStripSeparator3;
		private ToolStripSeparator toolStripSeparator4;
		private ToolStripStatusLabel toolStripStatusLabel;
		private ToolStrip tsHexEditor;
		private ToolStripButton tsbCopy;
		private ToolStripButton tsbCut;
		private ToolStripButton tsbFind;
		private ToolStripButton tsbOpen;
		private ToolStripButton tsbOptions;
		private ToolStripButton tsbPaste;
		private ToolStripButton tsbSave;

		#endregion Fields

		#region Constructors

		public HexEditor(ICEPluginHost host)
		{
			InitializeComponent();
			Host = host;
			TabText = "Hex Editor";
			tsHexEditor.Renderer = new Office2007Renderer();
		}

		#endregion Constructors

		#region Properties

		public bool AbleToCopy
		{
			get { return false; }
		}

		public bool AbleToCut
		{
			get { return false; }
		}

		public bool AbleToDelete
		{
			get { return false; }
		}

		public bool AbleToPaste
		{
			get { return false; }
		}

		public bool AbleToRedo
		{
			get { return false; }
		}

		public bool AbleToSave
		{
			get { return false; }
		}

		public bool AbleToSelectAll
		{
			get { return false; }
		}

		public bool AbleToUndo
		{
			get { return false; }
		}

		public string FileName
		{
			get { return ""; }
		}

		public ICEPluginHost Host
		{
			get; set;
		}

		public bool NeedsSaving
		{
			get { return false; }
		}

		public string Selection
		{
			get { return ""; }
		}

		#endregion Properties

		#region Methods

		public bool CloseTab()
		{
			return false;
		}

		public void Copy()
		{
		}

		public void Cut()
		{
		}

		public void Delete()
		{
		}

		public void Duplicate()
		{
		}

		public bool FindNext( Regex reg, bool searchUp)
		{
			return false;
		}

		public long GetByteIndex()
		{
			return Convert.ToInt64(nup.Value) - 1;
		}

		public void MarkAll( Regex reg)
		{
		}

		/// <summary>
		/// Opens a file.
		/// </summary>
		/// <param name="fileName">the file name of the file to open</param>
		public void OpenFile(string fileName)
		{
			if (!File.Exists(fileName)) {
				MessageBox.Show("File not exist!", "CodingEditor", MessageBoxButtons.OK, MessageBoxIcon.Information);
				return;
			}

			if (hexBox.ByteProvider != null) {
				if (CloseFile() == DialogResult.Cancel)
					return;
			}

			try {
				DynamicFileByteProvider dynamicFileByteProvider;
				try {
					// try to open in write mode
					dynamicFileByteProvider = new DynamicFileByteProvider(fileName);
					dynamicFileByteProvider.Changed += byteProvider_Changed;
					dynamicFileByteProvider.LengthChanged += byteProvider_LengthChanged;
				}
				catch (IOException) { // write mode failed
					try {
						// try to open in read-only mode
						dynamicFileByteProvider = new DynamicFileByteProvider(fileName, true);
						if (Program.ShowQuestion("The file is locked. Do you want to open it in read-only mode?") == DialogResult.No) {
							dynamicFileByteProvider.Dispose();
							return;
						}
					}
					catch (IOException) { // read-only also failed
						// file cannot be opened
						MessageBox.Show("File is locked by another process and cannot be opened!", "CodingEditor", MessageBoxButtons.OK,
						                MessageBoxIcon.Information);
						return;
					}
				}

				hexBox.ByteProvider = dynamicFileByteProvider;
				_fileName = fileName;

				DisplayText();

				UpdateFileSizeStatus();

				// Goto
				SetMaxByteIndex(hexBox.ByteProvider.Length);
				SetDefaultValue(hexBox.SelectionStart);
				nup.Select(0, nup.Value.ToString().Length);
			}
			catch (Exception ex1) {
				MessageBox.Show(ex1.Message, "CodingEditor", MessageBoxButtons.OK, MessageBoxIcon.Information);
				return;
			} finally {
				ManageAbility();
			}
		}

		public void Paste()
		{
		}

		public void Print(bool showDialog)
		{
		}

		public void Redo()
		{
		}

		public void ReplaceAll( Regex reg,   string replaceWith)
		{
		}

		public void ReplaceNext( Regex reg,  string replaceWith, bool searchUp)
		{
		}

		public void Save()
		{
		}

		public void SaveAs( string filePath)
		{
		}

		public void SelectAll()
		{
		}

		public void SelectWord(int line, int offset, int wordLeng)
		{
		}

		public void SetDefaultValue(long index)
		{
			nup.Value = index + 1;
		}

		public void SetMaxByteIndex(long maxIndex)
		{
			nup.Maximum = maxIndex + 1;
		}

		public void Undo()
		{
		}

		private static void hexBox_DragEnter(object sender, DragEventArgs e)
		{
			e.Effect = DragDropEffects.All;
		}

		private void CleanUp()
		{
			if (hexBox.ByteProvider != null) {
				var byteProvider = hexBox.ByteProvider as IDisposable;
				if (byteProvider != null)
					byteProvider.Dispose();
				hexBox.ByteProvider = null;
			}
			_fileName = null;
			DisplayText();
		}

		/// <summary>
		/// Closes the current file
		/// </summary>
		/// <returns>OK, if the current file was closed.</returns>
		private DialogResult CloseFile()
		{
			if (hexBox.ByteProvider == null)
				return DialogResult.OK;

			try {
				if (hexBox.ByteProvider != null && hexBox.ByteProvider.HasChanges()) {
					DialogResult res = MessageBox.Show("Do you want to save changes?",
					                                   "CodingEditor",
					                                   MessageBoxButtons.YesNoCancel,
					                                   MessageBoxIcon.Warning);

					if (res == DialogResult.Yes) {
						SaveFile();
						CleanUp();
					} else if (res == DialogResult.No) {
						CleanUp();
					} else if (res == DialogResult.Cancel) {
						return res;
					}

					return res;
				} else {
					CleanUp();
					return DialogResult.OK;
				}
			} finally {
				ManageAbility();
			}
		}

		/// <summary>
		/// Displays the file name in the Form´s text property
		/// </summary>
		private void DisplayText()
		{
			if (!string.IsNullOrEmpty(_fileName)) {
				const string textFormat = "{0}{1} - {2}";
				string sReadOnly = ((DynamicFileByteProvider) hexBox.ByteProvider).ReadOnly
				                   ? "[Read - Only]"
						   : "";
				string text = Path.GetFileName(_fileName);
				TabText = string.Format(textFormat, text, sReadOnly, "CodingEditor");
			} else {
				Text = "CodingEditor";
			}
		}

		/// <summary>
		/// Opens the Find dialog
		/// </summary>
		private void Find()
		{
			using (HexFind find = new HexFind())
			      {
				      if (find.ShowDialog() == DialogResult.OK) {
					      _findBuffer = find.GetFindBytes();
					      FindNext();
				      }
			      }
		}

		/// <summary>
		/// Find next match
		/// </summary>
		private void FindNext()
		{
			if (_findBuffer.Length == 0) {
				Find();
				return;
			}

			// start find process
			long res = hexBox.Find(_findBuffer, hexBox.SelectionStart + hexBox.SelectionLength);

			if (res == -1) { // -1 = no match
				Program.ShowMessage("Reached end of file");
			} else if (res == -2) {   // -2 = find was aborted
				return;
			} else { // something was found
				if (!hexBox.Focused)
					hexBox.Focus();
			}

			ManageAbility();
		}

		private void InitializeComponent()
		{
			System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(HexEditor));
			this.tsHexEditor = new System.Windows.Forms.ToolStrip();
			this.tsbOpen = new System.Windows.Forms.ToolStripButton();
			this.tsbSave = new System.Windows.Forms.ToolStripButton();
			this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
			this.tsbCut = new System.Windows.Forms.ToolStripButton();
			this.tsbCopy = new System.Windows.Forms.ToolStripButton();
			this.tsbPaste = new System.Windows.Forms.ToolStripButton();
			this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
			this.tsbFind = new System.Windows.Forms.ToolStripButton();
			this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
			this.tsbOptions = new System.Windows.Forms.ToolStripButton();
			this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
			this.hexBox = new Be.Windows.Forms.HexBox();
			this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
			this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
			this.statusStrip1 = new System.Windows.Forms.StatusStrip();
			this.toolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
			this.fileSizeToolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
			this.nup = new System.Windows.Forms.NumericUpDown();
			this.tsHexEditor.SuspendLayout();
			this.statusStrip1.SuspendLayout();
			((System.ComponentModel.ISupportInitialize)(this.nup)).BeginInit();
			this.SuspendLayout();
			//
			// tsHexEditor
			//
			this.tsHexEditor.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
			                                        this.tsbOpen,
			                                        this.tsbSave,
			                                        this.toolStripSeparator1,
			                                        this.tsbCut,
			                                        this.tsbCopy,
			                                        this.tsbPaste,
			                                        this.toolStripSeparator2,
			                                        this.tsbFind,
			                                        this.toolStripSeparator3,
			                                        this.tsbOptions,
			                                        this.toolStripSeparator4
							});
			this.tsHexEditor.Location = new System.Drawing.Point(0, 0);
			this.tsHexEditor.Name = "tsHexEditor";
			this.tsHexEditor.Size = new System.Drawing.Size(499, 25);
			this.tsHexEditor.TabIndex = 1;
			this.tsHexEditor.Text = "toolStrip1";
			//
			// tsbOpen
			//
			this.tsbOpen.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
			this.tsbOpen.Image = ((System.Drawing.Image)(resources.GetObject("tsbOpen.Image")));
			this.tsbOpen.ImageTransparentColor = System.Drawing.Color.Magenta;
			this.tsbOpen.Name = "tsbOpen";
			this.tsbOpen.Size = new System.Drawing.Size(23, 22);
			this.tsbOpen.Text = "Open";
			this.tsbOpen.Click += new System.EventHandler(this.tsbOpen_Click);
			//
			// tsbSave
			//
			this.tsbSave.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
			this.tsbSave.Image = (System.Drawing.Image)(resources.GetObject("tsbSave.Image"));
			this.tsbSave.ImageTransparentColor = System.Drawing.Color.Magenta;
			this.tsbSave.Name = "tsbSave";
			this.tsbSave.Size = new System.Drawing.Size(23, 22);
			this.tsbSave.Text = "Save";
			this.tsbSave.Click += new System.EventHandler(this.tsbSave_Click);
			//
			// toolStripSeparator1
			//
			this.toolStripSeparator1.Name = "toolStripSeparator1";
			this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
			//
			// tsbCut
			//
			this.tsbCut.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
			this.tsbCut.Image = ((System.Drawing.Image)(resources.GetObject("tsbCut.Image")));
			this.tsbCut.ImageTransparentColor = System.Drawing.Color.Magenta;
			this.tsbCut.Name = "tsbCut";
			this.tsbCut.Size = new System.Drawing.Size(23, 22);
			this.tsbCut.Text = "Cut";
			this.tsbCut.Click += new System.EventHandler(this.tsbCut_Click);
			//
			// tsbCopy
			//
			this.tsbCopy.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
			this.tsbCopy.Image = ((System.Drawing.Image)(resources.GetObject("tsbCopy.Image")));
			this.tsbCopy.ImageTransparentColor = System.Drawing.Color.Magenta;
			this.tsbCopy.Name = "tsbCopy";
			this.tsbCopy.Size = new System.Drawing.Size(23, 22);
			this.tsbCopy.Text = "Copy";
			this.tsbCopy.Click += new System.EventHandler(this.tsbCopy_Click);
			//
			// tsbPaste
			//
			this.tsbPaste.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
			this.tsbPaste.Image = ((System.Drawing.Image)(resources.GetObject("tsbPaste.Image")));
			this.tsbPaste.ImageTransparentColor = System.Drawing.Color.Magenta;
			this.tsbPaste.Name = "tsbPaste";
			this.tsbPaste.Size = new System.Drawing.Size(23, 22);
			this.tsbPaste.Text = "Paste";
			this.tsbPaste.Click += new System.EventHandler(this.tsbPaste_Click);
			//
			// toolStripSeparator2
			//
			this.toolStripSeparator2.Name = "toolStripSeparator2";
			this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
			//
			// tsbFind
			//
			this.tsbFind.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
			this.tsbFind.Image = ((System.Drawing.Image)(resources.GetObject("tsbFind.Image")));
			this.tsbFind.ImageTransparentColor = System.Drawing.Color.Magenta;
			this.tsbFind.Name = "tsbFind";
			this.tsbFind.Size = new System.Drawing.Size(23, 22);
			this.tsbFind.Text = "Find";
			this.tsbFind.Click += new System.EventHandler(this.tsbFind_Click);
			//
			// toolStripSeparator3
			//
			this.toolStripSeparator3.Name = "toolStripSeparator3";
			this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
			//
			// tsbOptions
			//
			this.tsbOptions.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
			this.tsbOptions.Image = ((System.Drawing.Image)(resources.GetObject("tsbOptions.Image")));
			this.tsbOptions.ImageTransparentColor = System.Drawing.Color.Magenta;
			this.tsbOptions.Name = "tsbOptions";
			this.tsbOptions.Size = new System.Drawing.Size(53, 22);
			this.tsbOptions.Text = "Options";
			this.tsbOptions.Click += new System.EventHandler(this.tsbOptions_Click);
			//
			// toolStripSeparator4
			//
			this.toolStripSeparator4.Name = "toolStripSeparator4";
			this.toolStripSeparator4.Size = new System.Drawing.Size(6, 25);
			//
			// hexBox
			//
			this.hexBox.AllowDrop = true;
			this.hexBox.Cursor = System.Windows.Forms.Cursors.Default;
			this.hexBox.Dock = System.Windows.Forms.DockStyle.Fill;
			this.hexBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
			this.hexBox.LineInfoForeColor = System.Drawing.Color.SlateGray;
			this.hexBox.LineInfoVisible = true;
			this.hexBox.Location = new System.Drawing.Point(0, 25);
			this.hexBox.Margin = new System.Windows.Forms.Padding(10);
			this.hexBox.Name = "hexBox";
			this.hexBox.ShadowSelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(60)))), ((int)(((byte)(188)))), ((int)(((byte)(255)))));
			this.hexBox.Size = new System.Drawing.Size(499, 211);
			this.hexBox.StringViewVisible = true;
			this.hexBox.TabIndex = 2;
			this.hexBox.UseFixedBytesPerLine = true;
			this.hexBox.VScrollBarVisible = true;
			//
			// statusStrip1
			//
			this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
			                                         this.toolStripStatusLabel,
			                                         this.fileSizeToolStripStatusLabel
							 });
			this.statusStrip1.Location = new System.Drawing.Point(0, 236);
			this.statusStrip1.Name = "statusStrip1";
			this.statusStrip1.Size = new System.Drawing.Size(499, 22);
			this.statusStrip1.TabIndex = 3;
			this.statusStrip1.Text = "statusStrip1";
			//
			// toolStripStatusLabel
			//
			this.toolStripStatusLabel.Name = "toolStripStatusLabel";
			this.toolStripStatusLabel.Size = new System.Drawing.Size(0, 17);
			//
			// fileSizeToolStripStatusLabel
			//
			this.fileSizeToolStripStatusLabel.Name = "fileSizeToolStripStatusLabel";
			this.fileSizeToolStripStatusLabel.Size = new System.Drawing.Size(0, 17);
			//
			// nup
			//
			this.nup.Location = new System.Drawing.Point(231, 3);
			this.nup.Name = "nup";
			this.nup.Size = new System.Drawing.Size(120, 20);
			this.nup.TabIndex = 4;
			//
			// HexEditor
			//
			this.ClientSize = new System.Drawing.Size(499, 258);
			this.Controls.Add(this.nup);
			this.Controls.Add(this.hexBox);
			this.Controls.Add(this.statusStrip1);
			this.Controls.Add(this.tsHexEditor);
			this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
			this.Name = "HexEditor";
			this.tsHexEditor.ResumeLayout(false);
			this.tsHexEditor.PerformLayout();
			this.statusStrip1.ResumeLayout(false);
			this.statusStrip1.PerformLayout();
			((System.ComponentModel.ISupportInitialize)(this.nup)).EndInit();
			this.ResumeLayout(false);
			this.PerformLayout();
		}

		/// <summary>
		/// Manages enabling or disabling of menu items and toolstrip buttons.
		/// </summary>
		private void ManageAbility()
		{
			if (hexBox.ByteProvider == null) {
				tsbSave.Enabled = false;
				tsbFind.Enabled = false;
			} else {
				tsbSave.Enabled = hexBox.ByteProvider.HasChanges();
				tsbFind.Enabled = true;
			}

			ManageAbilityForCopyAndPaste();
		}

		/// <summary>
		/// Manages enabling or disabling of menustrip items and toolstrip buttons for copy and paste
		/// </summary>
		private void ManageAbilityForCopyAndPaste()
		{
			tsbCopy.Enabled = hexBox.CanCopy();
			tsbCut.Enabled = hexBox.CanCut();
			tsbPaste.Enabled = hexBox.CanPaste();
		}

		private void Position_Changed(object sender, EventArgs e)
		{
			toolStripStatusLabel.Text = string.Format("Ln {0}    Col {1}",
			                                          hexBox.CurrentLine, hexBox.CurrentPositionInLine);
		}

		/// <summary>
		/// Saves the current file.
		/// </summary>
		private void SaveFile()
		{
			if (hexBox.ByteProvider == null)
				return;

			try {
				var dynamicFileByteProvider = hexBox.ByteProvider as DynamicFileByteProvider;
				dynamicFileByteProvider.ApplyChanges();
			}
			catch (Exception ex1) {
				MessageBox.Show(ex1.Message, "CodingEditor", MessageBoxButtons.OK, MessageBoxIcon.Information);
			} finally {
				ManageAbility();
			}
		}

		/// <summary>
		/// Updates the File size status label
		/// </summary>
		private void UpdateFileSizeStatus()
		{
			fileSizeToolStripStatusLabel.Text = hexBox.ByteProvider == null ? string.Empty : HexUtils.GetDisplayBytes(hexBox.ByteProvider.Length);
		}

		private void byteProvider_Changed(object sender, EventArgs e)
		{
			ManageAbility();
		}

		private void byteProvider_LengthChanged(object sender, EventArgs e)
		{
			UpdateFileSizeStatus();
		}

		private void hexBox_CurrentLineChanged(object sender, EventArgs e)
		{
			Position_Changed(sender, e);
		}

		private void hexBox_CurrentPositionInLineChanged(object sender, EventArgs e)
		{
			Position_Changed(sender, e);
		}

		private void hexBox_DragDrop(object sender, DragEventArgs e)
		{
			object oFileNames = e.Data.GetData(DataFormats.FileDrop);
			var fileNames = (string[])oFileNames;
			if (fileNames.Length == 1) {
				OpenFile(fileNames[0]);
			}
		}

		private void hexBox_SelectionLengthChanged(object sender, EventArgs e)
		{
			ManageAbilityForCopyAndPaste();
		}

		private void hexBox_SelectionStartChanged(object sender, EventArgs e)
		{
			ManageAbilityForCopyAndPaste();
		}

		private void nup_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Enter) {
				hexBox.SelectionStart = GetByteIndex();
				hexBox.SelectionLength = 1;
				hexBox.Focus();
			}
		}

		private void tsbCopy_Click(object sender, EventArgs e)
		{
			hexBox.Copy();
		}

		private void tsbCut_Click(object sender, EventArgs e)
		{
			hexBox.Cut();
		}

		private void tsbFind_Click(object sender, EventArgs e)
		{
			Find();
		}

		private void tsbOpen_Click(object sender, EventArgs e)
		{
			if (openFileDialog.ShowDialog() == DialogResult.OK) {
				OpenFile(openFileDialog.FileName);
			}
		}

		private void tsbOptions_Click(object sender, EventArgs e)
		{
			using (HexOptions hexOptions = new HexOptions())
			      {
				      hexOptions.ShowDialog();
			      }
		}

		private void tsbPaste_Click(object sender, EventArgs e)
		{
			hexBox.Paste();
		}

		private void tsbSave_Click(object sender, EventArgs e)
		{
			SaveFile();
		}

		#endregion Methods
	}
}